`include "decoder.v"

module control(rstN, inst_header, currentCycle, clear, mux_ctrl, reg_enable, alu_ctrl);

localparam INST_DECODE = 2'd0, //Decodificacao da instrucao.
           OPERAND1_FETCH = 2'd1, //Seleciona o primeiro operando e o armazena em A.
           OPERAND2_FETCH = 2'd2, //Seleciona o segundo operando e o envia direto para a ALU.
           REG_STORE = 2'd3; /*O resultado presente em R (saida da ALU) e transferido para
                               o reg de destino.*/


localparam REP = 3'b111,
           LDI = 3'b101,
           ADD = 3'b000,
           SUB = 3'b001,
           NAN = 3'b010,
           OUT = 3'b100;

localparam ALU_BYPASS = 2'd0,
           ALU_ADD = 2'd1,
           ALU_SUB = 2'd2,
           ALU_NAND = 2'd3;

input rstN;
input[8:0] inst_header; //primeiros 9 bits da inst.
input[1:0] currentCycle; //ciclo atual da inst. Vem do contador.

output reg clear; //reseta o contador.

//Saidas one-hot. bits 7:0 correspondem aos reg7:reg0, respectivamente.
output reg[9:0] mux_ctrl; //seleciona qual a fonte de entrada do mux (1-hot). [8] = immediate, [9] = R
output reg[9:0] reg_enable; //ativa enable nos registradores do datapath. (1-hot) [8] = A, [9] = R

output reg[1:0] alu_ctrl; //seleciona operacao da ALU. 

//Sub-campos de inst_header;
reg[2:0] opcode;
reg[2:0] rx;
reg[2:0] ry;

//Converte as representacoes binarias de Rx e Ry em
//representacoes one-hot:
wire[7:0] dx_out, dy_out;
decoder dx(rx, dx_out);
decoder dy(ry, dy_out);

always @(*) begin
    opcode = inst_header[8:6];
    rx = inst_header[5:3];
    ry = inst_header[2:0];
end

always @(*) begin
    alu_ctrl = ALU_BYPASS; //valor padrão
    
    case(opcode)
        3'b000 : alu_ctrl = ALU_ADD;
        3'b001 : alu_ctrl = ALU_SUB;
        3'b010 : alu_ctrl = ALU_NAND;
    endcase

end


//Always block para settar as saidas de controle
always@(*) begin

    //Setta valores padrão dos sinais para evitar latch inferring:
    clear = 1'b1;
    mux_ctrl = 10'd0;
    reg_enable = 10'd0;

    case(currentCycle)
        INST_DECODE : begin
        //Não faz nada.
        end
        OPERAND1_FETCH : begin
            if(opcode != LDI) begin
                mux_ctrl[7:0] = dy_out;
            end else begin
                mux_ctrl[8] = 1'b1; //seleciona o imediato.
            end
            reg_enable[8] = 1'b1; //habilita A para receber o primeiro operando.
        end
        OPERAND2_FETCH : begin
            //Obs: coloca Rx na segunda entrada ALU.
            //Para LDI e OUT essa segunda entrada e ignorada.
            mux_ctrl[7:0] = dx_out;
            reg_enable[9] = 1'b1; //ativa R para escrita no proximo posedge
        end
        REG_STORE : begin
            mux_ctrl[9] = 1'b1; //seleciona R no mux.
            reg_enable[7:0] = dx_out; //habilita registrador de destino para escrita.

        end
    endcase
end
endmodule
